package com.fangs.lunbotu;


import com.fangs.lunbotu.utils.ProxyCacheUtils;
import com.ivans.videocache.HttpUrlSource;
import com.ivans.videocache.ProxyCacheException;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
 * desc   : 视频预加载类
 * remark : 预加载成功才启动播放器开始播放 （此项目开屏启动页有用到此类）
 * version: 1.0
 */
public class PreCacher {

    private final ExecutorService preCacheExecutor = Executors.newFixedThreadPool(8);

    private final Map<String, PreCacheCallable> mCacheCallableMap;

    private PreCacher() {
        mCacheCallableMap = new HashMap<>();
    }

    public static PreCacher getInstance() {
        return InstanceHolder.mInstance;
    }

    /**
     * 开始缓存
     *
     * @param url        缓存的地址 (指结合本地代理的ProxyUrl 通过videocache库的getProxyUrl可获得)
     * @param cacheBytes 缓存大小（值大于0才生效 否则默认全部完整缓存 直至int最大值）
     */
    public synchronized void startCache(String url, int cacheBytes) {
        if (mCacheCallableMap.containsKey(url)) {
            return;
        }
        PreCacheCallable cacheCallable = new PreCacheCallable(url, cacheBytes);
        mCacheCallableMap.put(url, cacheCallable);
        preCacheExecutor.submit(cacheCallable);
    }

    /**
     * 手动停止缓存
     *
     * @param url
     */
    public synchronized void stopCache(String url) {
        PreCacheCallable cacheCallable = mCacheCallableMap.get(url);
        if (cacheCallable != null) {
            cacheCallable.stop();
            mCacheCallableMap.remove(url);
        }
    }

    /**
     * 结束所有缓存操作
     */
    public void stopAll() {
        for (PreCacheCallable cacheCallable : mCacheCallableMap.values()) {
            cacheCallable.stop();
        }
        mCacheCallableMap.clear();
    }

    private void removePreCacheCallable(String url) {
        PreCacheCallable cacheCallable = mCacheCallableMap.get(url);
        if (cacheCallable != null) {
            mCacheCallableMap.remove(url);
        }
    }

    class PreCacheCallable implements Callable<Boolean> {

        String mUrl;                       //缓存地址
        int mCacheSize = Integer.MAX_VALUE;//准备缓存的大小，单位bytes
        boolean mStoped = false;                   //停止缓存
        private final Object mStopLock = new Object();
        HttpUrlSource mSource;

        public PreCacheCallable(String url, int cacheBytes) {
            mUrl = url;
            if (cacheBytes > 0) {
                mCacheSize = cacheBytes;
            }
        }

        @Override
        public Boolean call() {
            return connectToPoxyServer(mUrl);
        }


        public void stop() {
            synchronized (mStopLock) {
                mStoped = true;
            }
        }

        private boolean isStoped() {
            return mStoped;
        }

        //TODO 需不需要考虑retry等
        private boolean connectToPoxyServer(String url) {
            synchronized (mStopLock) {
                if (isStoped()) {
                    return false;
                }
            }
            mSource = new HttpUrlSource(url);
            try {
                mSource.open(0);
                byte[] buffer = new byte[ProxyCacheUtils.DEFAULT_BUFFER_SIZE];
                int readBytes;
                int offset = 0;
                while ((readBytes = mSource.read(buffer)) != -1) {
                    synchronized (mStopLock) {
                        if (isStoped()) {
                            break;
                        }
                    }
                    offset += readBytes;
                    if (offset >= mCacheSize) {
                        break;
                    }
                }
            } catch (ProxyCacheException e) {
                e.printStackTrace();
                try {
                    mSource.close();
                } catch (ProxyCacheException proxyCacheException) {
                    proxyCacheException.printStackTrace();
                }
                return false;
            } finally {
                try {
                    mSource.close();
                    removePreCacheCallable(url);
                } catch (ProxyCacheException e) {
                    e.printStackTrace();
                }
            }
            return true;
        }
    }

    static class InstanceHolder {
        private static final PreCacher mInstance = new PreCacher();
    }
}
